package org.xm.sk.controller;

import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.MessageSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestOperations;
import org.xm.sk.common.ToolsUtil;
import org.xm.sk.exception.BaseException;
import org.xm.sk.exception.WxAuthException;
import org.xm.sk.module.IdBitWorker;
import org.xm.sk.service.data.BaseService;
import org.xm.sk.service.security.JwtTokenUtil;
import org.xm.sk.vo.ob.AuthRequest;
import org.xm.sk.vo.ob.ResultMsg;
import org.xm.sk.vo.tb.Organ;
import org.xm.sk.vo.tb.Role;
import org.xm.sk.vo.tb.User;

import com.fasterxml.jackson.databind.ObjectMapper;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;

@SuppressWarnings("unchecked")
@Api(tags = { "auth" })
@RestController
public class AuthController {

    @Value("${jwt.header}")
    private String header;
    @Value("${jwt.tokenHead}")
    private String tokenHead;

    @Value("${wx.login.cl.appId}")
    private String cl_appId;
    @Value("${wx.login.cl.secret}")
    private String cl_secret;
    @Value("${wx.login.hr.appId}")
    private String hr_appId;
    @Value("${wx.login.hr.secret}")
    private String hr_secret;
    @Value("${wx.login.openIdUrl}")
    private String openIdUrl;
    
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private BaseService<User> userService;
    @Autowired
    private BaseService<Role> roleService;
    @Autowired
    private BaseService<Organ> organService;
    @Autowired
    private IdBitWorker idBitWorker;
    @Autowired
    private MessageSource messages;
    @Autowired
    private RestOperations restOperations;

    @ApiOperation(value = "账号获取鉴权", notes = "通过POST账号信息登录系统获取鉴权Token信息，注意：Token信息不在返回的信息体内，而是在放回的信息头部内，字段authorization的内容")
    @ApiImplicitParams({
        @ApiImplicitParam(paramType = "header", dataType = "String", name = "authorization", defaultValue = "Bearer token", value = "令牌", required = false),
        @ApiImplicitParam(name = "authRequest", value = "账号鉴权对象", required = true, dataType = "AuthRequest")
    })
    @RequestMapping(value = "${jwt.route.authentication.path.cl}", method = RequestMethod.POST)
    public UserDetails createAuthenticationToken(
            @RequestBody AuthRequest authRequest, HttpServletResponse rsp) {
        UsernamePasswordAuthenticationToken upToken = new UsernamePasswordAuthenticationToken(
                authRequest.getCrednm(),
                authRequest.getCredco());
        // Perform the security
        final Authentication authentication = authenticationManager
                .authenticate(upToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);

        final UserDetails userDetails = userDetailsService
                .loadUserByUsername(authRequest.getCrednm());
        final String token = jwtTokenUtil.generateToken(userDetails);
        rsp.setHeader(header, tokenHead + token);
        return userDetails;
    }
    
    @ApiOperation(value = "客户端通过微信账号获取鉴权", notes = "通过POST微信账号CODE信息登录系统获取鉴权Token信息，注意：Token信息不在返回的信息体内，而是在放回的信息头部内，字段authorization的内容")
    @ApiImplicitParams({
        @ApiImplicitParam(paramType = "header", dataType = "String", name = "authorization", defaultValue = "Bearer token", value = "令牌", required = false),
        @ApiImplicitParam(name = "code", value = "微信账号CODE信息", required = true, dataType = "string", paramType = "path")
    })
    @RequestMapping(value = "${jwt.route.authentication.path.cl}/{code}", method = RequestMethod.GET)
    public ResultMsg createAuthenticationToken4Cl(
            @PathVariable String code, HttpServletResponse rsp) throws Exception {
        wxlogin(code, rsp, cl_appId, cl_secret, "ROLE_CLIENT");
        return new ResultMsg("200", messages.getMessage("success", null, "SUCCESS", null), new Date());
    }
    
    @ApiOperation(value = "发型师端通过微信账号获取鉴权", notes = "通过POST微信账号CODE信息登录系统获取鉴权Token信息，注意：Token信息不在返回的信息体内，而是在放回的信息头部内，字段authorization的内容")
    @ApiImplicitParams({
        @ApiImplicitParam(paramType = "header", dataType = "String", name = "authorization", defaultValue = "Bearer token", value = "令牌", required = false),
        @ApiImplicitParam(name = "code", value = "微信账号CODE信息", required = true, dataType = "string", paramType = "path")
    })
    @RequestMapping(value = "${jwt.route.authentication.path.hr}/{code}", method = RequestMethod.GET)
    public ResultMsg createAuthenticationToken4Hr(
            @PathVariable String code, HttpServletResponse rsp) throws Exception {
        wxlogin(code, rsp, hr_appId, hr_secret, "ROLE_HAIRDRE");
        return new ResultMsg("200", messages.getMessage("success", null, "SUCCESS", null), new Date());
    }
    
    public void wxlogin(String code, HttpServletResponse rsp, String appId,
            String secret, String roleName)
            throws Exception {
        String token = null;
        UserDetails userDetails = null;
        ObjectMapper mapper = new ObjectMapper();
        Object result = restOperations.getForObject(openIdUrl, String.class, appId, secret, code);
        Map<?, ?> map = mapper.readValue(result.toString(), Map.class);
        if (null != map.get("errmsg")) {
            throw new WxAuthException(map.get("errmsg").toString());
        }
        
        // 如果没有错误信息，那么获取
        // openid 用户唯一标识
        // session_key 会话密钥
        String openId = map.get("openid").toString();
        String sessionKey = map.get("session_key").toString();
        // 然后用openid查找用户
        Set<User> users = (Set<User>) userService.findByName(openId);
        // 如果存在，就获取token和用户信息返回
        if (null != users && !users.isEmpty()) {
            if (1 < users.size()) {
                throw new BaseException("用户数据异常！");
            }
            User user = users.iterator().next();
            user.setPsw(ToolsUtil.desdecrypt(sessionKey));
            userDetails = userDetailsService
                    .loadUserByUsername(user.getName());
            token = jwtTokenUtil.generateToken(userDetails);
            userService.update(user);
        }
        // 如果不存在，增加用户记录，默认是客户，然后，获取token和用户信息返回
        else {
            /*String wxAccessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appId}&secret={secret}";
            result = restOperations.getForObject(wxAccessTokenUrl, Object.class, appId, secret);
            map = mapper.readValue(mkJsonStr(result), Map.class);
            if (null != map.get("errmsg")) {
                throw new WxAuthException(map.get("errmsg").toString());
            }
            String accessToken = map.get("access_token").toString();
            String wxUserInfoUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token={accessToken}&openid={openId}&lang=zh_CN";
            result = restOperations.getForObject(wxUserInfoUrl, Object.class, accessToken, openId);
            map = mapper.readValue(mkJsonStr(result), Map.class);
            if (null != map.get("errmsg")) {
                throw new WxAuthException(map.get("errmsg").toString());
            }*/
            User user = new User();
            //user.setGender(Gender.values()[Integer.valueOf(map.get("sex").toString())]);
            //user.setCity(map.get("city").toString());
            //user.setProvince(map.get("province").toString());
            //user.setCountry(map.get("country").toString());
            user.setCode(idBitWorker.nextUserNum());
            user.setName(openId);
            user.setPsw(ToolsUtil.desdecrypt(sessionKey));
            Set<Role> roles = (Set<Role>) roleService.findByName(roleName);
            if (null != roles && 0 < roles.size()) {
                user.setRoles(roles);
            }
            Organ organ = (Organ) organService.findById(1);
            user.setOrgan(organ);
            user.setLastPasswordResetDate(new Date());
            user.setMemo("[ROLE: " + roleName + "]");
            userService.add(user);
            userDetails = userDetailsService
                    .loadUserByUsername(openId);
            token = jwtTokenUtil.generateToken(userDetails);
        }
        rsp.setHeader(header, tokenHead + token);
    }

    @ApiOperation(value = "刷新鉴权Token信息", notes = "持Token信息来获取新的Token信息以延迟Token的时效，注意：Token信息不在返回的信息体内，而是在放回的信息头部内，字段authorization的内容")
    @RequestMapping(value = "${jwt.route.authentication.refresh}", method = RequestMethod.GET)
    public ResultMsg refreshAndGetAuthenticationToken() {
        return new ResultMsg("200", messages.getMessage("success", null, "SUCCESS", null), new Date());
    }

    @ApiOperation(value = "注册获取账号", notes = "通过POST待注册的账号信息获取系统反馈信息")
    @ApiImplicitParams({
        @ApiImplicitParam(paramType = "header", dataType = "String", name = "authorization", defaultValue = "Bearer token", value = "令牌", required = false),
        @ApiImplicitParam(name = "adduser", value = "账号对象", required = true, dataType = "User")
    })
    @RequestMapping(value = "${jwt.route.authentication.register}", method = RequestMethod.POST)
    public User register(
            @RequestBody User adduser)
            throws Exception {
        final String username = adduser.getName();
        Set<User> users = (Set<User>) userService.findByName(username);
        if (null != users && 0 < users.size()) {
            throw new BaseException(
                    messages.getMessage("register.error.username", null,
                            "Duplicate user name.", null));
        }
        adduser.setPsw(ToolsUtil.desdecrypt(adduser.getPsw()));
        if (null == adduser.getRoles() || adduser.getRoles().isEmpty()) {
            Set<Role> roles = (Set<Role>) roleService.findByName("ROLE_CLIENT");
            if (null != roles && 0 < roles.size()) {
                adduser.setRoles(roles);
            }
        } else {
            Set<Role> roles = new HashSet<Role>();
            for (Role role : adduser.getRoles()) {
                if (null != role.getId() && 0 < role.getId()) {
                    roles.add((Role) roleService.findById(role.getId()));
                }
            }
            adduser.setRoles(roles);
        }
        Organ organ = (Organ) organService.findById(1);
        adduser.setOrgan(organ);
        return userService.add(adduser);
    }
    
    public String mkJsonStr(Object result) {
        String res = "";
        if (!result.toString().contains("\":")) {
            if (result.toString().contains(", ")) {
                res = result.toString().replace("{", "{\"").replace("}", "\"}").replace("=", "\": \"").replace(", ", "\", \"");
            } else {
                res = result.toString().replace("{", "{\"").replace("}", "\"}").replace("=", "\": \"").replace(",", "\", \"");
            }
        } else {
            res = result.toString();
        }
        return res;
    }

}
